package com.vbobot.demo.spring.nacos.client;

import java.io.IOException;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;


/**
 * @author sihua
 * @date 2021/11/11
 */
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    public RestTemplate httpClientRestTemplate(RestTemplateBuilder builder,
            HttpComponentsClientHttpRequestFactory httpClientRequestFactory) {
        return builder
                .setConnectTimeout(10 * 1000)
                .setReadTimeout(120 * 1000)
                .requestFactory(() -> httpClientRequestFactory)
                .additionalInterceptors(new HeaderTransmitConfig.HeaderRequestInterceptor())
                .errorHandler(new RestTemplateResponseErrorHandler())
                .build();
    }

    @Bean
    @LoadBalanced
    public RestTemplate okHttpRestTemplate(RestTemplateBuilder builder, OkHttp3ClientHttpRequestFactory okHttpRequestFactory) {
        return builder
                .setConnectTimeout(10 * 1000)
                .setReadTimeout(120 * 1000)
                .requestFactory(() -> okHttpRequestFactory)
                // 注册restTemplate的拦截
                .additionalInterceptors(new HeaderTransmitConfig.HeaderRequestInterceptor(), new SupportGetBodyInterceptor())
                .errorHandler(new RestTemplateResponseErrorHandler())
                .build();
    }

    @Bean
    @LoadBalanced
    public RestTemplate okHttpRestTemplate1(RestTemplateBuilder builder, OkHttp3ClientHttpRequestFactory okHttpRequestFactory1) {
        return builder
                .setConnectTimeout(10 * 1000)
                .setReadTimeout(120 * 1000)
                .requestFactory(() -> okHttpRequestFactory1)
                // 注册restTemplate的拦截
                .additionalInterceptors(new HeaderTransmitConfig.HeaderRequestInterceptor())
                .errorHandler(new RestTemplateResponseErrorHandler())
                .build();
    }

    @Bean
    @LoadBalanced
    public RestTemplate okHttpRestTemplate2(RestTemplateBuilder builder, OkHttp3ClientHttpRequestFactory okHttpRequestFactory2) {
        return builder
                .setConnectTimeout(10 * 1000)
                .setReadTimeout(120 * 1000)
                .requestFactory(() -> okHttpRequestFactory2)
                // 注册restTemplate的拦截
                .additionalInterceptors(new HeaderTransmitConfig.HeaderRequestInterceptor(), new SupportGetBodyInterceptor())
                .errorHandler(new RestTemplateResponseErrorHandler())
                .build();
    }

    @Bean
    @LoadBalanced
    public RestTemplate notSupportGetWithBodyRestTemplate(RestTemplateBuilder builder,
            OkHttp3ClientHttpRequestFactory notSupportGetWithBodyOkHttpRequestFactory) {
        return builder
                .setConnectTimeout(10 * 1000)
                .setReadTimeout(120 * 1000)
                .requestFactory(() -> notSupportGetWithBodyOkHttpRequestFactory)
                // 注册restTemplate的拦截
                .additionalInterceptors(new HeaderTransmitConfig.HeaderRequestInterceptor())
                .errorHandler(new RestTemplateResponseErrorHandler())
                .build();
    }

    @Bean
    public HttpComponentsClientHttpRequestFactory httpClientRequestFactory(CloseableHttpClient closeableHttpClient) {
        return new HttpComponentsClientRestfulHttpRequestFactory(closeableHttpClient);
    }

    @Bean
    public CloseableHttpClient closeableHttpClient() {
        //配置可参考：https://www.programcreek.com/java-api-examples/?class=org.springframework.http.client.HttpComponentsClientHttpRequestFactory&method=setBufferRequestBody

        //Httpclient连接池，长连接保持1800秒
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(1800, TimeUnit.SECONDS);

        //设置总连接数
        connectionManager.setMaxTotal(200);

        //设置同路由的并发数
        connectionManager.setDefaultMaxPerRoute(50);
        return HttpClients.custom()
                .setConnectionManager(connectionManager)
                .evictIdleConnections(30, TimeUnit.SECONDS)
                .disableAutomaticRetries()
                .build();
    }

    @Bean
    public OkHttp3ClientHttpRequestFactory okHttpRequestFactory(OkHttpClient okHttpClient) {
        return new OkHttp3ClientHttpRequestFactory(okHttpClient);
    }

    /**
     * Interceptor 标记，event改
     */
    @Bean
    public OkHttp3ClientHttpRequestFactory okHttpRequestFactory1(OkHttpClient okHttpClient1) {
        return new OkHttp3ClientHttpRequestFactory(okHttpClient1);
    }

    @Bean
    public OkHttp3ClientHttpRequestFactory okHttpRequestFactory2(OkHttpClient okHttpClient2) {
        return new OkHttp3ClientHttpRequestFactory(okHttpClient2);
    }

    @Bean
    public OkHttp3ClientHttpRequestFactory notSupportGetWithBodyOkHttpRequestFactory(OkHttpClient notSupportGetWithBodyOkHttpClient) {
        return new OkHttp3ClientHttpRequestFactory(notSupportGetWithBodyOkHttpClient);
    }

    @Bean
    public OkHttpClient okHttpClient2() {
        // 设置连接池参数，最大空闲连接数200，空闲连接存活时间10s
        ConnectionPool connectionPool = new ConnectionPool(200, 20, TimeUnit.SECONDS);

        return new Builder()
                .retryOnConnectionFailure(false)
                .connectionPool(connectionPool)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                // 注册拦截器
                .addInterceptor(new SupportGetBodyInterceptor2())
                // 注册事件监听
                .eventListener(new SupportGetBodyEventListener2())
                .build();
    }

    @Bean
    public OkHttpClient okHttpClient1() {
        // 设置连接池参数，最大空闲连接数200，空闲连接存活时间10s
        ConnectionPool connectionPool = new ConnectionPool(200, 20, TimeUnit.SECONDS);

        return new Builder()
                .retryOnConnectionFailure(false)
                .connectionPool(connectionPool)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                // 注册拦截器
                .addInterceptor(new SupportGetBodyInterceptor1())
                // 注册事件监听
                .eventListener(new SupportGetBodyEventListener1())
                .build();
    }

    @Bean
    public OkHttpClient okHttpClient() {
        // 设置连接池参数，最大空闲连接数200，空闲连接存活时间10s
        ConnectionPool connectionPool = new ConnectionPool(200, 20, TimeUnit.SECONDS);

        return new Builder()
                .retryOnConnectionFailure(false)
                .connectionPool(connectionPool)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                // 注册拦截器
                .addInterceptor(new SupportGetBodyInterceptor())
                // 注册事件监听
                .eventListener(new SupportGetBodyEventListener())
                .build();
    }

    @Bean
    public OkHttpClient notSupportGetWithBodyOkHttpClient() {
        // 设置连接池参数，最大空闲连接数200，空闲连接存活时间10s
        ConnectionPool connectionPool = new ConnectionPool(200, 20, TimeUnit.SECONDS);

        return new Builder()
                .retryOnConnectionFailure(false)
                .connectionPool(connectionPool)
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .writeTimeout(120, TimeUnit.SECONDS)
                .build();
    }

    @Slf4j
    private static final class RestTemplateResponseErrorHandler extends DefaultResponseErrorHandler {

        @Override
        public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
            log.warn("RestTemplate call error, url:{}", url);
            super.handleError(url, method, response);
        }
    }

    public static class HttpComponentsClientRestfulHttpRequestFactory extends HttpComponentsClientHttpRequestFactory {

        public HttpComponentsClientRestfulHttpRequestFactory(HttpClient httpClient) {
            super(httpClient);
        }

        @Override
        protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
            if (httpMethod == HttpMethod.GET) {
                return new HttpGetRequestWithEntity(uri);
            }
            return super.createHttpUriRequest(httpMethod, uri);
        }
    }

    private static final class HttpGetRequestWithEntity extends HttpEntityEnclosingRequestBase {
        public HttpGetRequestWithEntity(final URI uri) {
            super.setURI(uri);
        }

        @Override
        public String getMethod() {
            return HttpMethod.GET.name();
        }

    }
}
